package com.eugeneMa.util;

import com.eugeneMa.common.Config;
import io.jsonwebtoken.*;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.Map;
import java.util.Random;

/**
 * 权限校验工具类
 * 包括验证码生成，JWT生成与校验等
 *
 * @author Eugenema
 * @date 2022/2/8 11:34
 */
public class AuthUtil {
    /** 日志 */
    private static Logger logger = Logger.getLogger(AuthUtil.class);
    /** 随机数 */
    private static Random random = new Random();
    /** 验证码宽度 */
    private static int width = 200;
    /** 验证码高度 */
    private static int height = 69;
    /** 验证码长度 */
    private static int codeLength = 4;

    /**
     * 生成4位随机验证码
     * 以jpeg格式输出到指定输出流中，宽高为：200，69；生成4位数字的验证码图片，输出到指定流
     * 若验证码字符为空，则生成随机验证码
     *
     * @author Eugenema
     * @date 2022/2/8 11:39
     *
     * @param outputStream 输出流
     * @param alreadyCode 验证码字符
     *
     * @return 验证码字符串，若出现异常，返回null
     **/
    public static String getAuthCode(OutputStream outputStream, String alreadyCode) {
        try {
            /** 验证码字符 */
            String authCode = alreadyCode;
            if (StringUtil.isEmpty(authCode)) {
                authCode = getAuthCodeString();
            }

            //图片的类型，使用RGB类型
            BufferedImage codeImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            //获取画笔对象
            Graphics2D graphics = (Graphics2D) codeImage.getGraphics();
            //背景色：白色，设置画笔颜色为白色，并且填充矩形区域
            graphics.setColor(Color.WHITE);
            graphics.fillRect(0, 0, width, height);
            /** 旋转角度，小于30度 */
            int degree;
            /** 距离顶部位置 */
            int y;

            //绘制验证码
            char[] codeChars = authCode.toCharArray();
            for (int i = 0; i < codeChars.length; i++) {
                //设置字体、加粗、倾斜、大小为30，设置字的颜色随机，随机旋转
                graphics.setFont(new Font(Font.DIALOG, Font.BOLD + Font.ITALIC, 30));
                graphics.setColor(getColor());
                //旋转最多30度
                degree = random.nextInt() % 30;
                y = random.nextInt(20);
                graphics.rotate(degree * Math.PI / 180, 5 + (i * 48), y + 25);

                //将每个字符画到图片，每个字符间距48，距离顶部45
                graphics.drawString(String.valueOf(codeChars[i]), 5 + (i * 48), y + 25);

                //摆正画笔（上一步旋转了）
                graphics.rotate(-degree * Math.PI / 180, 5 + (i * 48), y + 25);
            }

            //随机画8条干扰线
            for (int i = 0; i < 8; i++) {
                graphics.setColor(getColor());
                int x1 = random.nextInt(width);
                int y1 = random.nextInt(height);
                int x2 = random.nextInt(width);
                int y2 = random.nextInt(height);
                graphics.drawLine(x1, y1, x2, y2);
            }

            //添加噪点
            for (int i = 0; i < 30; i++) {
                int x1 = random.nextInt(width);
                int y1 = random.nextInt(height);
                graphics.setColor(getColor());
                graphics.fillRect(x1, y1, 2, 2);
            }

            //将缓存的图片输出到响应输出流中
            //参数1：要输出的缓存图片，参数2：格式，参数3：输出到哪个流中
            ImageIO.write(codeImage, "jpeg", outputStream);
            return authCode;
        } catch (IOException e) {
            logger.error("生成验证码异常！", e);
            return null;
        }
    }

    /**
     * 根据签名生成JWT令牌
     *
     * @author Eugenema
     * @date 2022/2/19 17:49
     *
     * @param payload 载荷
     *
     * @return 加密后的JWT，加密失败，返回null
     **/
    public static String createJWT(Map<String, Object> payload){
        try {
            JwtBuilder eugeneMa = Jwts.builder()
                    //载荷
                    .addClaims(payload)
                    //唯一编号
                    .setId(String.valueOf(System.currentTimeMillis()))
                    //主题
                    .setSubject("emPerson")
                    //创建时间
                    .setIssuedAt(new Date())
                    //签名
                    .signWith(SignatureAlgorithm.HS256, Config.properties.get("signature"));
            return eugeneMa.compact();
        } catch (Exception e) {
            logger.error("加密JWT异常！", e);
            return null;
        }
    }

    /**
     * 解析JWT
     *
     * @author Eugenema
     * @date 2022/2/19 18:47
     *
     * @param jwt 要解密的jwt
     *
     * @return 解密后的载荷，解析失败返回null
     **/
    public static Claims parseJwt(String jwt){
        if (StringUtil.isEmpty(jwt)) {
            return null;
        }

        try {
            Jws<Claims> claimsJws = Jwts.parser()
                                    //签名
                                    .setSigningKey(Config.properties.get("signature"))
                                    //jwt
                                    .parseClaimsJws(jwt);
            return claimsJws.getBody();
        } catch (Exception e) {
            logger.error("解析JWT异常！", e);
            return null;
        }
    }

    /**
     * 获取随机颜色
     *
     * @author Eugenema
     * @date 2022/2/8 11:37
     *
     * @return 随机生成的颜色
     **/
    private static Color getColor() {
        //红，绿，蓝取值0-255，随机生成 0-255之间数
        int r = random.nextInt(256);
        int g = random.nextInt(256);
        int b = random.nextInt(256);
        return new Color(r, g, b);
    }

    /**
     * 获取随机四位字符，作为验证码
     * 取值范围为配置文件中的codeRange
     *
     * @author Eugenema
     * @date 2022/2/20 22:05
     *
     *
     * @return 验证码
     **/
    private static String getAuthCodeString() {
        StringBuilder authCode = new StringBuilder();
        for (int i = 0; i < codeLength; i++) {
            //随机从 0 到 验证码取数区域的长度 之间去取索引
            int index = random.nextInt(Config.properties.get("codeRange").length());
            //从验证码取数区域中获得索引对应字符
            char codeChar = Config.properties.get("codeRange").charAt(index);
            //追加到验证码字符串中
            authCode.append(codeChar);
        }
        return authCode.toString();
    }
}
